
#include <BSocket>
#include <BLibrary>
#include <BReadWrite>
#include "IWindow.h"
#include "IGlobal.h"
#include "ISockets.h"
#include "member_BObject.h"
#include "member_BWidget.h"
#include "member_BDrag.h"
#include "member_BStyle.h"
#include "member_BPixmap.h"
#include "member_BScene.h"
#include "member_BAudioInput.h"
#include "member_BAudioOutput.h"
#undef near
#undef far

using namespace BWE;

clock_t			app_tick = 0;
bool			app_done = false;
int				app_code = 0;
int				app_frequency = 30;
int 			app_realFrequency = 0;
double			app_beginTime = 0.0;
double			app_lastRenderTime = 0.0;
double			app_clickInterval = 0.6;
#ifdef linux
pthread_t		app_handle = 0;
Display*		app_display = 0;
#endif
#ifdef _WIN32
HANDLE			app_handle = 0;
#endif

BValue					app_clipBoard;
BString					app_libPath;
BArray<BString>			app_pluginPaths;
BArray<BString>			app_fontPaths;
BArray<BString>			app_dataPaths;

BHolder<BStyle>			app_style;
BList<IWindowHolder>	app_windows;
BList<BSocket*>			app_sockets;
BList<BScene*>			app_scenes;
BList<BAudioInput*>		app_audioInputs;
BList<BAudioOutput*>	app_audioOutputs;

BList<bplugin>			app_plugins;
BMap<BString, BString>	app_extAlias;

#ifdef linux
pthread_mutex_t		mutex;
#else
CRITICAL_SECTION	criticalSection;
#endif
BMap<BObject*, SlotPackArray>	app_slotPackMap;
BMap<BObject*, SignalWaitArray>	app_SignalWaitMap;

bool App_InitGlobalMutex()
{
#ifdef linux
	int error = pthread_mutex_init(&mutex, 0);
#endif
#ifdef _WIN32
	InitializeCriticalSection(&criticalSection);
#endif
	return true;
}
bool App_LockGlobalMutex()
{
#ifdef linux
	int error = pthread_mutex_lock(&mutex);
	return error == 0;
#else
	EnterCriticalSection(&criticalSection);
	return true;
#endif
}
bool App_UnlockGlobalMutex()
{
#ifdef linux
	int error = pthread_mutex_unlock(&mutex);
	return error == 0;
#else
	LeaveCriticalSection(&criticalSection);
	return true;
#endif
}

bool App_AttachWindow(BWidget* widget)
{
#ifdef linux
	if(!app_display)
		return false;
#endif
	if (!widget)
		return false;
	IWindow* window = 0;
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* w = app_windows[i];
		if (w->boss == 0)
		{
			window = app_windows[i];
			window->boss = widget;
			break;
		}
	}
	if (!window)
	{
		window = new IWindow(widget);
	}
	widget_member(widget)->window = window;
	widget_member(widget)->dirtyStyle = true;
	return true;
}
bool App_DetachWindow(BWidget* widget)
{
	if (!widget)
		return false;
	IWindow* window = widget_member(widget)->window;
	if (window)
	{
		window->close_time = clock();
		window->hideWindow();
		window->boss = 0;
		widget_member(widget)->window = 0;
	}
	return true;
}
BWidget* App_MainWindow()
{
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* w = app_windows[i];
		if (w->boss)
		{
			return w->boss;
		}
	}
	return 0;
}

void App_CycleBegin()
{
	app_beginTime = bTime();
}
void App_Event(IWindow* window)
{
#ifdef linux
	Display* display = app_display;
	if (window)
		display = window->display;
	XEvent event;
	while (XPending(display))
	{
		XNextEvent(display, &event);
		const XAnyEvent& any = (const XAnyEvent&)event;
		if (window)
		{
			if (any.window == window->hwnd)
			{
				window->procEvent(event);
			}
		}
		else
		{
			for (int i = 0; i < app_windows.size(); i++)
			{
				IWindow* win = app_windows[i];
				if (any.window == win->hwnd)
				{
					win->procEvent(event);
					break;
				}
			}
		}
	};
#endif
#ifdef _WIN32
	MSG msg;
	if (window)
	{
		while (PeekMessageA(&msg, window->hwnd, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	else
	{
		while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
#endif
	while (app_slotPackMap.size())
	{
		App_LockGlobalMutex();
		auto it = app_slotPackMap.begin();
		BObject* object = it.key();
		SlotPackArray slotPacks = it.value();
		app_slotPackMap.erase(it);
		App_UnlockGlobalMutex();
		for (int i = 0; i < slotPacks.size(); i++)
		{
			SlotPack& pack = slotPacks[i];
			if (pack.object)
			{
				object_member(object)->post(pack.object, pack.signal, pack.value);
				object_member(object)->post(pack.object, pack.strsig, pack.value);
			}
			else
			{
				object_member(object)->emit(pack.signal, pack.value);
				object_member(object)->emit(pack.strsig, pack.value);
			}
		}
	}
}
void App_Event(BDrag* drag)
{
	if (!drag)
		return;
	static BDragEvent dragEvent;
	dragEvent.setDrag(drag);
#ifdef linux
	
#endif
	
#ifdef _WIN32
	MSG msg;
	while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
	{
		if (msg.message == 0)
			break;
		if (msg.message == WM_QUIT)
			break;
		TranslateMessage(&msg);
		for (int i = 0; i < app_windows.size(); i++)
		{
			IWindow* window = app_windows[i];
			if (window->hwnd != msg.hwnd)
				continue;
			if (msg.message == WM_MOUSEMOVE)
			{
				BPoint absolutePos((short)LOWORD(msg.lParam), (short)HIWORD(msg.lParam));
				dragEvent.setAbsolutePos(absolutePos);
				window->procDragMove(dragEvent);
				break;
			}
			if (msg.message == WM_LBUTTONUP)
			{
				dragEvent.setType(Event_DragDrop);
				window->procDragDrop(dragEvent);
				drag_member(drag)->over = true;
				dragEvent.setDrag(0);
				break;
			}
			DispatchMessage(&msg);
			break;
		}
	}
#endif
}
void App_Update()
{
	for (int i = 0; i < app_audioInputs.size(); i++)
	{
		BAudioInput* audioInput = app_audioInputs[i];
		audioinput_member(audioInput)->emitSignals();
	}
	BReal time = bTime();
	for (int i = 0; i < app_scenes.size(); i++)
	{
		BScene* scene = app_scenes[i];
		if (scene->enabled())
		{
			scene_member(scene)->update(time);
		}
	}
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (!window->boss)
			continue;
		if (window->boss->enabled())
		{
			window->update();
		}
	}
}
void App_Frame()
{
	if (app_windows.empty())
		return;
	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (!window->boss)
			continue;
		if (window->boss->enabled() && window->boss->visible())
		{
			window->fresh();
			window->frame();
		}
	}
}
void App_CycleEnd()
{
	static int frameCount = 0;
	frameCount++;
	clock_t now = clock();
	if (now - app_tick > 1000)
	{
		app_tick = now;
		app_realFrequency = frameCount;
		frameCount = 0;
	}

	for (int i = 0; i < app_windows.size(); i++)
	{
		IWindow* window = app_windows[i];
		if (!window->boss && now - window->close_time > 360000)
		{
			app_windows.remove(i--);
		}
	}

	App_SelectSocket();

	double timePass = bTime() - app_beginTime;
	double sleepTime = 1.0 / app_frequency - timePass;
	if (sleepTime > 0)
	{
		bSleep(sleepTime);
	}
}

